package com.zhiwei.codesecurity.common.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.serializer.ValueFilter;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.http.HttpEntity;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import org.apache.commons.lang3.StringUtils;

public class HttpUtils {
    private static Logger logger = LoggerFactory.getLogger(HttpUtils.class);

    private static final ObjectMapper mapper = new ObjectMapper();

    public static <T> T remote(String url, Object params, Class<T> retClass, Integer timeout) throws Exception {
        String rlt = httpPost(url, JSONObject.toJSONString(params, new ValueFilter() {
            @Override
            public Object process(Object obj, String s, Object v) {
                if (v == null) {
                    return null;
                }
                if (v.getClass() == double.class || v.getClass() == Double.class) {
                    return new BigDecimal(v.toString());
                }
                return v;
            }
        }, new SerializerFeature[0]), timeout);
        logger.info("====<<< httpPost, rlt={}", rlt);

        if (StringUtils.isEmpty(rlt)) {
            return null;
        }
        return JSONObject.parseObject(rlt, retClass);
    }

    public static <T> T remote(String url, Object params, TypeReference<T> retClass, Integer timeout) throws Exception {
        String rlt = httpPost(url, JSONObject.toJSONString(params, new ValueFilter() {
            @Override
            public Object process(Object obj, String s, Object v) {
                if (v == null) {
                    return null;
                }
                if (v.getClass() == double.class || v.getClass() == Double.class) {
                    return new BigDecimal(v.toString());
                }
                return v;
            }
        }, new SerializerFeature[0]), timeout);
        return JSONObject.parseObject(rlt, retClass);
    }

    public static <T> T remoteUseJackson(String url, Object params, Class<T> retClass, Integer timeout) throws Exception {
        String rlt = httpPost(url, mapper.writeValueAsString(params), timeout);
        return mapper.readValue(rlt, retClass);
    }


    public static String httpPost(String url, String param, Integer timeout) throws Exception {
        logger.info("====== http request begin ======");
        //日志监控统计耗时&&调用次数
        CloseableHttpClient httpclient = HttpClients.createDefault();
        HttpPost httppost = new HttpPost(url);
        if (timeout != null) {
            RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(timeout).setConnectTimeout(timeout).build();//设置请求和传输超时时间
            httppost.setConfig(requestConfig);
        }
        StringEntity strEntity = new StringEntity(param, "utf-8");//解决中文乱码问题
        strEntity.setContentEncoding("UTF-8");
        strEntity.setContentType("application/json");
        httppost.setEntity(strEntity);
        // 请求内容过大,不输出日志
        if (param.length() < 4000) {
            logger.info("===== http request url={},params={}", url, param);
        }

        CloseableHttpResponse response = httpclient.execute(httppost);
        logger.info("=====>>> http response url={},rlt={}", url, response);

        try {
            HttpEntity entity = response.getEntity();
            InputStream in = entity.getContent();
            BufferedReader reader = new BufferedReader(new InputStreamReader(in));
            StringBuffer stringBuffer = new StringBuffer();
            String str = "";
            while ((str = reader.readLine()) != null) {
                stringBuffer.append(str);
            }
            in.close();

            if (stringBuffer.length() < 4000) {
                logger.info("=====<<< http response stringBuff url={},rlt={}", url, stringBuffer);
            } else {
                logger.info("=====<<< http response stringBuff url={},rlt={}", url, stringBuffer.substring(0, 4000) + "...");
            }

            //记录方法耗时
//            try {
//                Metric.HistogramEnd();
//            } catch (Exception e) {
//                logger.error("http request metric end error!", e);
//            }
            return stringBuffer.toString();
        } finally {
            response.close();
        }

    }


    public static String httpGet(String url) {
        CloseableHttpClient client = HttpClients.createDefault();
        HttpGet get = new HttpGet(url);

        String result = null;
        try {
            CloseableHttpResponse response = client.execute(get);
            HttpEntity entity = response.getEntity();
            result = EntityUtils.toString(entity);
            response.close();
        } catch (IOException e) {
            logger.error("httpGet error. url={}", url, e);
        }
        logger.info("=====<<< http url={} rlt={}", url, result);
        return result;
    }


    /**
     * https请求
     *
     * @param url
     * @param timeout
     * @return
     * @throws Exception
     */
    public static <T> T httpsPost(String url, Object params, Class<T> retClass, String header, String serverIp, String serverPort, Integer timeout) throws Exception {
        logger.info("====== http request begin ======");
        CloseableHttpClient httpclient = HttpClients.createDefault();

        String paramStr = JSON.toJSONString(params);
        try {
            httpclient = registerSSL(serverIp, "TLS",
                    Integer.parseInt(serverPort), url);
        } catch (Exception e1) {
            e1.printStackTrace();
            throw new RuntimeException("初始化httpclient异常" + e1.getMessage());
        }

        HttpPost httppost = new HttpPost(url);
        if (timeout != null) {
            RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(timeout).setConnectTimeout(timeout).build();//设置请求和传输超时时间
            httppost.setConfig(requestConfig);
        }
        StringEntity strEntity = new StringEntity(paramStr, "utf-8");//解决中文乱码问题
        httppost.setEntity(strEntity);

        httppost.addHeader("Authorization", header);
        httppost.addHeader("Accept", "application/json");
        httppost.addHeader("Content-Type", "application/json;charset=utf-8");
        // 请求内容过大,不输出日志
        if (paramStr.length() < 4000) {
            logger.info("===== http request url={},params={}", url, paramStr);
        }

        CloseableHttpResponse response = httpclient.execute(httppost);
        logger.info("=====>>> http response url={},rlt={}", url, response);
        try {
            HttpEntity entity = response.getEntity();
            InputStream in = entity.getContent();
            BufferedReader reader = new BufferedReader(new InputStreamReader(in));
            StringBuffer stringBuffer = new StringBuffer();
            String str = "";
            while ((str = reader.readLine()) != null) {
                stringBuffer.append(str);
            }
            in.close();

            if (stringBuffer.length() < 4000) {
                logger.info("=====<<< http response stringBuff url={},rlt={}", url, stringBuffer);
            } else {
                logger.info("=====<<< http response stringBuff url={},rlt={}", url, stringBuffer.substring(0, 4000) + "...");
            }
            return mapper.readValue(stringBuffer.toString(), retClass);
        } finally {
            response.close();
        }
    }


    public static DefaultHttpClient registerSSL(String hostname, String protocol, int port, String scheme)
            throws NoSuchAlgorithmException, KeyManagementException {
        DefaultHttpClient httpclient = new DefaultHttpClient();

        SSLContext ctx = SSLContext.getInstance(protocol);

        X509TrustManager tm = new X509TrustManager() {
            public void checkClientTrusted(X509Certificate[] chain, String authType)
                    throws java.security.cert.CertificateException {
            }

            public void checkServerTrusted(X509Certificate[] chain, String authType)
                    throws java.security.cert.CertificateException {
                if ((chain == null) || (chain.length == 0))
                    throw new IllegalArgumentException("null or zero-length certificate chain");
                if ((authType == null) || (authType.length() == 0))
                    throw new IllegalArgumentException("null or zero-length authentication type");

                boolean br = false;
                Principal principal = null;
                for (X509Certificate x509Certificate : chain) {
                    principal = x509Certificate.getSubjectX500Principal();
                    if (principal != null) {
                        br = true;
                        return;
                    }
                }
                if (!(br))
                    throw new CertificateException("服务端证书验证失败！");
            }

            public X509Certificate[] getAcceptedIssuers() {
                return new X509Certificate[0];
            }

        };
        ctx.init(null, new TrustManager[]{tm}, new SecureRandom());

        SSLSocketFactory socketFactory = new SSLSocketFactory(ctx, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
        Scheme sch = new Scheme(scheme, port, socketFactory);

        httpclient.getConnectionManager().getSchemeRegistry().register(sch);
        return httpclient;
    }
}
